//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Zoom levels handling.
//

# include "StdAfx.h"
# include "Resource.h"
# include "Levels.h"
# include "Status.h"
# include "Scale.h"
# include "Map.h"
# include "MapView.h"
# include "Menu.h"
# include "Globals.h"

static size_t g_cVisibleLevel = -1;

# pragma const_seg (".text")

const size_t c_cMaxBits = 26;

const float g_fBaseScales [] = {
	     0,
	     30.f/c_pixels_per_cm, // 26
//	     50.f/c_pixels_per_cm, // 25
	     80.f/c_pixels_per_cm, // 25
	    120.f/c_pixels_per_cm, // 24
//	    200.f/c_pixels_per_cm, // 23
	    300.f/c_pixels_per_cm, // 23
	    500.f/c_pixels_per_cm, // 22
//	    800.f/c_pixels_per_cm, // 21
	   1200.f/c_pixels_per_cm, // 21
	   2000.f/c_pixels_per_cm, // 20
	   3000.f/c_pixels_per_cm, // 19
//	   5000.f/c_pixels_per_cm, // 18
	   8000.f/c_pixels_per_cm, // 18
	  12000.f/c_pixels_per_cm, // 17
//	  20000.f/c_pixels_per_cm, // 16
	  30000.f/c_pixels_per_cm, // 16
	  50000.f/c_pixels_per_cm, // 15
//	  80000.f/c_pixels_per_cm, // 14
	 120000.f/c_pixels_per_cm, // 14
	 200000.f/c_pixels_per_cm, // 13
//	 300000.f/c_pixels_per_cm, // 12
	 500000.f/c_pixels_per_cm, // 12
	 800000.f/c_pixels_per_cm, // 11
	1200000.f/c_pixels_per_cm, // 10
};

const size_t c_cBaseScales = sizeof (g_fBaseScales)/sizeof (g_fBaseScales [0]);

# pragma const_seg ()

static
size_t _GetLevelScaleBits (size_t _cLevelIdx) {
	const map_level_t & level = g_map.Levels [_cLevelIdx];
	assert (level.btBits >= level.btBitsRangeTo);
	return level.btBitsRangeTo;
}

size_t GetZoomLevel (float _fScale, char * _strOut) {
	if (g_cVisibleLevel != -1) {
		if (_strOut)
			::sprintf (_strOut, " *Level%d", g_cVisibleLevel);
		return g_cVisibleLevel;
	}

	CCritSec::CLock lock (g_cs);

	const size_t cLevels = g_map.Levels.size ();
	if (cLevels <= 1) {
		if (_strOut)
			::sprintf (_strOut, " No levels");
		return 0;
	}

	// Find nearest zoom level for the given scale.
	size_t cScale;
	for (cScale = 1; cScale < c_cBaseScales; ++ cScale)
		if (_fScale <= g_fBaseScales [cScale])
			break;
	-- cScale;
	assert (cScale < c_cBaseScales);
	if (cScale >= c_cBaseScales) {
		if (_strOut)
			::sprintf (_strOut, " Error level");
		return 0;
	}
	const size_t cBits = c_cMaxBits - cScale;

	if (cBits > _GetLevelScaleBits (0)) {
		// NOTE: Show most detailed level up to 0 to avoid empty map.
		if (_strOut)
			::sprintf (_strOut, "<Level0");
		return 0;
	}

	// Find map level for the given zoom level.
	for (size_t cLevel = 0; cLevel < cLevels - 1; ++ cLevel) {
		const size_t cLevelBits = _GetLevelScaleBits (cLevel);
		if (cLevelBits <= cBits) {
			if (_strOut)
				::sprintf (_strOut, " =Level%d", cLevel);
			return cLevel;
		}
	}

	if (cBits < _GetLevelScaleBits (cLevels - 2)) {
		// NOTE: Show less detailed level up to infinity to avoid empty map.
		if (_strOut)
			::sprintf (_strOut, ">Level%d", cLevels - 2);
		return cLevels - 2;
	}

	if (_strOut)
		::sprintf (_strOut, " No levels");
	return 0;
}

bool LevelIsVisible (size_t _cLevel, size_t _cEndLevel) {
	if (g_cVisibleLevel != -1) {
		//
		// NOTE: some level is forced to be visible exclusively (regardless of scale).
		//

		if (_cLevel < _cEndLevel)
			return _cLevel <= g_cVisibleLevel && g_cVisibleLevel <= _cEndLevel;
		else
			return _cLevel == g_cVisibleLevel;
	} else {
		//
		// NOTE: visible level is selected automaticaly depending on scale.
		//

		CCritSec::CLock lock (g_cs);

		const size_t cLevels = g_map.Levels.size ();
		if (cLevels == 0)
			return true;

		if (_cEndLevel < _cLevel)
			_cEndLevel = _cLevel;
		for (size_t cLevel = _cLevel; cLevel <= _cEndLevel; ++ cLevel) {
			// NOTE: last level should be empty.
			if (cLevel >= cLevels - 1)
				return false;

			const size_t cScale0 = c_cMaxBits - _GetLevelScaleBits (cLevel) + 1;

			if (cLevel == cLevels - 2)
				// Show less detailed level up to infinity.
				if (g_fBaseScales [cScale0] < g_fScale)
					return true;
			if (cLevel == 0) {
				// Show most detailed level up to 0.
				if (g_fScale <= g_fBaseScales [cScale0])
					return true;
			} else {
				assert (cLevel >= 1);
				const size_t cScale1 = c_cMaxBits - _GetLevelScaleBits (cLevel - 1) + 1;
				if (g_fBaseScales [cScale1] < g_fScale && g_fScale <= g_fBaseScales [cScale0])
					return true;
			}
		}

		return false;
	}
}

/////////////////////////////////////////////////////////////

size_t GetVisibleLevel () {
	return g_cVisibleLevel;
}

bool SetVisibleLevel (size_t _cLevel) {
	if (g_cVisibleLevel == _cLevel)
		return false;

	size_t cLevels;
	{
		CCritSec::CLock lock (g_cs);
		cLevels = g_map.Levels.size ();
	}

	if (_cLevel != -1 && _cLevel >= cLevels) {
		MakeLevelsMenu ();
		UpdateMenu ();
		return false;
	}

	g_cVisibleLevel = _cLevel;

	return true;
}

/////////////////////////////////////////////////////////////

# pragma const_seg (".text")

const size_t c_cMaxLevels = 10;

static const WORD g_wSetLevelCmd [c_cMaxLevels] = {
	ID_VIEW_LEVEL0,
	ID_VIEW_LEVEL1,
	ID_VIEW_LEVEL2,
	ID_VIEW_LEVEL3,
	ID_VIEW_LEVEL4,
	ID_VIEW_LEVEL5,
	ID_VIEW_LEVEL6,
	ID_VIEW_LEVEL7,
	ID_VIEW_LEVEL8,
	ID_VIEW_LEVEL9,
};

# pragma const_seg ()

void MakeLevelsMenu () {
	const HMENU hMenu = ::GetMenu (g_hWnd);
	const HMENU hViewMenu   = ::GetSubMenu (hMenu,     smView);
	const HMENU hmenuLevels = ::GetSubMenu (hViewMenu, smViewLevels);

	size_t cLevels;
	{
		CCritSec::CLock lock (g_cs);
		cLevels = g_map.Levels.size ();
	}

	// Disable levels missing in the map.
	for (size_t cLevel = 0; cLevel < c_cMaxLevels; ++ cLevel)
		::EnableMenuItem (hmenuLevels, g_wSetLevelCmd [cLevel], MF_BYCOMMAND | (cLevel >= cLevels ? MF_GRAYED : MF_ENABLED));
	// Disable the less detailed level.
	// NOTE: this one is useless because is empty by definition.
	::EnableMenuItem (hmenuLevels, g_wSetLevelCmd [cLevels - 1], MF_BYCOMMAND | MF_GRAYED); 

	// Check 'Auto'.
	::CheckMenuRadioItem (hMenu, ID_VIEW_LEVELS_AUTO, ID_VIEW_LEVELS_AUTO, ID_VIEW_LEVELS_AUTO, MF_BYCOMMAND);
}

void UpdateLevelsMenu () {
	const HMENU hMenu = ::GetMenu (g_hWnd);

	if (g_cVisibleLevel == -1) {
		// Uncheck all levels.
		for (size_t cLevel = 0; cLevel < c_cMaxLevels; ++ cLevel)
			::CheckMenuItem (hMenu, g_wSetLevelCmd [cLevel], MF_BYCOMMAND | MF_UNCHECKED);
		// Check 'Auto'.
		::CheckMenuRadioItem (hMenu, ID_VIEW_LEVELS_AUTO, ID_VIEW_LEVELS_AUTO, ID_VIEW_LEVELS_AUTO, MF_BYCOMMAND);
	} else {
		// Uncheck 'Auto'.
		::CheckMenuItem (hMenu, ID_VIEW_LEVELS_AUTO, MF_BYCOMMAND | MF_UNCHECKED);
		// Uncheck all levels.
		for (size_t cLevel = 0; cLevel < c_cMaxLevels; ++ cLevel)
			::CheckMenuItem (hMenu, g_wSetLevelCmd [cLevel], MF_BYCOMMAND | MF_UNCHECKED);

		// Check specific level.
		::CheckMenuRadioItem (hMenu, g_wSetLevelCmd [g_cVisibleLevel], g_wSetLevelCmd [g_cVisibleLevel], g_wSetLevelCmd [g_cVisibleLevel], MF_BYCOMMAND);
	}
}